package com.sc.nft.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sc.nft.config.sand.SandPayUtil;
import com.sc.nft.dao.DepositWithdrawalRecordDao;
import com.sc.nft.dao.ExpenditureRecordDao;
import com.sc.nft.dao.RechargeAmountDao;
import com.sc.nft.dao.UserWalletDao;
import com.sc.nft.entity.*;
import com.sc.nft.entity.dto.SaveWithdrawRecordCauseDTO;
import com.sc.nft.entity.vo.*;
import com.sc.nft.enums.*;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.service.DepositWithdrawalRecordService;
import com.sc.nft.sup.ErrorCode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 服务接口实现
 *
 * @author Sun
 * @since 2022-09-07 12:59:49
 * @description 由 Mybatisplus Code Generator 创建
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class DepositWithdrawalRecordServiceImpl implements DepositWithdrawalRecordService {
    private final DepositWithdrawalRecordDao depositWithdrawalRecordDao;
    private final Redisson redisson;
    private final UserWalletDao userWalletDao;
    private final ExpenditureRecordDao expenditureRecordDao;
    private final RechargeAmountDao rechargeAmountDao;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approval(Long id, boolean passStatus, String remark) {
        RLock lock = redisson.getLock(RedisKeyEnum.WITHDRAW_APPROVAL_LOCK.getKey()+id);
        try{
            boolean res = lock.tryLock(3, 10, TimeUnit.SECONDS);
            if (res) {
                DepositWithdrawalRecord depositWithdrawalRecord = depositWithdrawalRecordDao.getById(id);
                Assert.notNull(depositWithdrawalRecord,()->new GlobalRunTimeException(ErrorCode.DRAW_NOT_FOUND));
                Assert.isTrue(depositWithdrawalRecord.getType()== DepositWithdrawalRecordTypeEnum.WITHDRAW,()->new GlobalRunTimeException(ErrorCode.DRAW_NOT_FOUND));
                if(passStatus){
                    Assert.isTrue(depositWithdrawalRecord.getStatus()== DepositWithdrawalRecordStatusEnum.APPROVAL && depositWithdrawalRecord.getStatus()== DepositWithdrawalRecordStatusEnum.APPROVAL,()->new GlobalRunTimeException(ErrorCode.DRAW_STATUS_ERROR));
                    approvalPass(depositWithdrawalRecord);
                }else{
                    Assert.isTrue(depositWithdrawalRecord.getStatus()== DepositWithdrawalRecordStatusEnum.APPROVAL,()->new GlobalRunTimeException(ErrorCode.DRAW_STATUS_ERROR));
//                    Assert.isTrue(StrUtil.isNotBlank(remark),()->new GlobalRunTimeException(ErrorCode.APPROVAL_FAIL_REMARK));
                    approvalFail(depositWithdrawalRecord,remark);
                }
            }else{
                throw new GlobalRunTimeException(ErrorCode.LATER_TRY);
            }
        } catch (InterruptedException e) {
            log.warn("审批等待超时");
        } finally {
            lock.unlock();
        }
    }



    @Override
    public Page<NoAuditRechargeRecordVO> getNoAuditRechargeRecord(int pageNo, int pageSize) {
        return depositWithdrawalRecordDao.getNoAuditRechargeRecord(pageNo,pageSize);
    }

    @Override
    public Page<NoRefundRechargeRecordVO> getNoRefundRechargeRecord(int pageNo, int pageSize) {
        return depositWithdrawalRecordDao.getNoRefundRechargeRecord(pageNo,pageSize);
    }

    @Override
    public NoRefundRechargeRecordByIdVO getNoRefundRechargeRecordById(Long id) {
        return depositWithdrawalRecordDao.getNoRefundRechargeRecordById(id);
    }

    @Override
    public NoAuditRechargeRecordByIdVO getNoAuditRechargeRecordById(Long id) {
        return depositWithdrawalRecordDao.getNoAuditRechargeRecordById(id);
    }










    @Override
    public Page<WithdrawOrRechargeRecordVO> withdrawalsRecord(Long userId, String calendar, Integer pageNo, Integer pageSize) {
        return depositWithdrawalRecordDao.withdrawalsOrRechargeRecord(userId,calendar,pageNo,pageSize,DepositWithdrawalRecordTypeEnum.WITHDRAW);
    }

    @Override
    public Page<WithdrawOrRechargeRecordVO> rechargeRecord(Long userId, String calendar, Integer pageNo, Integer pageSize) {
        return depositWithdrawalRecordDao.withdrawalsOrRechargeRecord(userId,calendar,pageNo,pageSize,DepositWithdrawalRecordTypeEnum.RECHARGE);
    }

    public void approvalPass(DepositWithdrawalRecord depositWithdrawalRecord){
        depositWithdrawalRecord.setApprovalTime(DateTime.now());
        String transferTime = DateTime.now().toString("yyyyMMddHHmmss");
        depositWithdrawalRecord.setTransferTime(transferTime);
        JSONObject result = SandPayUtil.drawRequest(SandPayUtil.getDrawRequest(transferTime,depositWithdrawalRecord.getOrderNo(),depositWithdrawalRecord.getRealAmount(),
                depositWithdrawalRecord.getCardNo(),depositWithdrawalRecord.getName()));
        if(StrUtil.equals(result.getString("resultFlag"),"0")){ //调用衫德成功
            withdrawSuccess(depositWithdrawalRecord);
        }else  if(StrUtil.equals(result.getString("resultFlag"),"1")){ //调用衫德成功
            depositWithdrawalRecord.setStatus(DepositWithdrawalRecordStatusEnum.FAIL);
            depositWithdrawalRecord.setRemark(result.getString("respDesc"));
            depositWithdrawalRecord.setCause(result.getString("respDesc"));
            depositWithdrawalRecord.updateById();
        }else{
            depositWithdrawalRecord.setStatus(DepositWithdrawalRecordStatusEnum.FAIL);
            depositWithdrawalRecord.setRemark(result.getString("respDesc"));
            depositWithdrawalRecord.setCause(result.getString("respDesc"));
            depositWithdrawalRecord.updateById();
        }
    }

    @Override
    public Page<RechargeRecordVO> getRechargeRecord(Date startTime, Date endTime, String orderNo, String payStatus, String userTel, int pageNo, int pageSize) {
        return depositWithdrawalRecordDao.getRechargeRecord(startTime, endTime, orderNo, payStatus, userTel, pageNo, pageSize);
    }



    @Override
    public void saveWithdrawRecordCause(SaveWithdrawRecordCauseDTO request) {
        depositWithdrawalRecordDao.saveWithdrawRecordCause(request);
    }

    @Override
    public List<RechargeAmount> getRechargeAmount() {
        return rechargeAmountDao.list();
    }

    public void withdrawSuccess(DepositWithdrawalRecord depositWithdrawalRecord) {
        depositWithdrawalRecord.setStatus(DepositWithdrawalRecordStatusEnum.SUCCESS);
        depositWithdrawalRecord.setPayStatus(DepositWithdrawalRecordPayStatusEnum.SUCCESS);
        depositWithdrawalRecord.updateById();
        depositWithdrawalRecord.setPayStatus(DepositWithdrawalRecordPayStatusEnum.SUCCESS);

        Long userId = depositWithdrawalRecord.getUserId();
        UserWallet userWallet = userWalletDao.getByUserId(userId);

        //插入账单明细表
        ExpenditureRecord expenditureRecord=new ExpenditureRecord();
        expenditureRecord.setUserId(depositWithdrawalRecord.getUserId());
        expenditureRecord.setAmount(depositWithdrawalRecord.getAmount());
        expenditureRecord.setCharge(depositWithdrawalRecord.getCharge());
        expenditureRecord.setRealAmount(depositWithdrawalRecord.getRealAmount());
        expenditureRecord.setType(ExpenditureRecordTypeEnum.WITHDRAW);
        expenditureRecord.setBeforeBalance(userWallet.getBalance().add(depositWithdrawalRecord.getAmount()));
        expenditureRecord.setAfterBalance(userWallet.getBalance());
        expenditureRecord.setOrderId(0L);
        expenditureRecordDao.insert(expenditureRecord);
        userWalletDao.withdrawSuccess(depositWithdrawalRecord.getUserId(),depositWithdrawalRecord.getAmount());
    }

    private void approvalFail(DepositWithdrawalRecord depositWithdrawalRecord, String remark){
        depositWithdrawalRecord.setStatus(DepositWithdrawalRecordStatusEnum.FAIL);
        depositWithdrawalRecord.setRemark(remark);
        depositWithdrawalRecord.setCause(remark);
        depositWithdrawalRecord.setApprovalTime(DateTime.now());
        depositWithdrawalRecord.updateById();
    }

}